from ctypes import *
import struct
import time
import sys
import os
from tqdm import tqdm
import argparse

VCI_USBCAN2 = 4
STATUS_OK = 1
# 其中 Timing0=0x03 和 Timing1=0x1C 设置了125kbps的波特率。
# 以下是一些常用的波特率配置参考：
# 1000kbps (1Mbps):
# Timing0 = 0x00
# Timing1 = 0x14
# 500kbps:
# Timing0 = 0x00
# Timing1 = 0x1C
# 250kbps:
# Timing0 = 0x01
# Timing1 = 0x1C
# 125kbps:
# Timing0 = 0x03
# Timing1 = 0x1C
# 100kbps:
# Timing0 = 0x04
# Timing1 = 0x1C
# CAN OTA 相关定义
CAN_OTA_CMD_ID = 0x400   # 命令帧基础ID
CAN_OTA_RESP_ID = 0x500  # 响应帧基础ID
CAN_OTA_DATA_ID = 0x600  # 数据帧基础ID

# 数据块传输相关定义
MAX_BLOCK_SIZE = 1024        # 最大数据块大小
MAX_PACKETS_PER_BLOCK = 256  # 每个数据块最大包数 (2048/8=256)
BLOCK_RETRY_COUNT = 3        # 数据块重传次数

# CAN OTA 命令定义
CAN_OTA_CMD_START = 0xA0
CAN_OTA_CMD_DATA = 0xA1
CAN_OTA_CMD_END = 0xA2
CAN_OTA_CMD_ABORT = 0xA3
CAN_OTA_CMD_VERIFY = 0xA4
CAN_OTA_CMD_BLOCK_START = 0xA5
CAN_OTA_CMD_BLOCK_END = 0xA6

# CAN OTA 响应定义
CAN_OTA_RESP_OK = 0x00
CAN_OTA_RESP_ERROR = 0x01
CAN_OTA_RESP_READY = 0x02
CAN_OTA_RESP_VERIFY = 0x03
CAN_OTA_RESP_RETRY = 0x04

class VCI_INIT_CONFIG(Structure):  
    _fields_ = [("AccCode", c_uint),
                ("AccMask", c_uint),
                ("Reserved", c_uint),
                ("Filter", c_ubyte),
                ("Timing0", c_ubyte),
                ("Timing1", c_ubyte),
                ("Mode", c_ubyte)
                ]  

class VCI_CAN_OBJ(Structure):  
    _fields_ = [("ID", c_uint),
                ("TimeStamp", c_uint),
                ("TimeFlag", c_ubyte),
                ("SendType", c_ubyte),
                ("RemoteFlag", c_ubyte),
                ("ExternFlag", c_ubyte),
                ("DataLen", c_ubyte),
                ("Data", c_ubyte*8),
                ("Reserved", c_ubyte*3)
                ]

class CANOTAUpdater:
    def __init__(self, device_ids=None):
        # 加载DLL
        self.canDLL = windll.LoadLibrary('./can_update_tool/ControlCAN.dll')
        self.device_type = VCI_USBCAN2
        self.device_ind = 0
        self.can_ind = 0
        self.cmd_seq = 0  # 命令序号计数器
        self.device_ids = device_ids if device_ids else [0]  # 默认设备ID为0
        
    def get_cmd_id(self, device_id):
        """生成命令帧ID"""
        return CAN_OTA_CMD_ID + (device_id & 0x0F)
        
    def get_data_id(self, device_id):
        """生成数据帧ID"""
        return CAN_OTA_DATA_ID + (device_id & 0x0F)
        
    def get_resp_id(self, device_id):
        """生成响应帧ID"""
        return CAN_OTA_RESP_ID + (device_id & 0x0F)
        
    def wait_all_responses(self, expected_resp=CAN_OTA_RESP_OK, timeout_ms=3000):
        """等待所有设备的响应"""
        responses = {}
        start_time = time.time()
        
        while len(responses) < len(self.device_ids):
            if (time.time() - start_time) * 1000 >= timeout_ms:
                print('\n部分设备响应超时:')
                # 打印未响应的设备
                unresponded_devices = set(self.device_ids) - set(responses.keys())
                print(f'未响应设备: {list(unresponded_devices)}')
                print(f'已响应设备: {list(responses.keys())}')
                
                # 询问用户是否继续
                while True:
                    choice = input('\n是否继续升级已响应的设备? (y/n): ').lower()
                    if choice == 'y':
                        # 更新设备列表为已响应的设备
                        self.device_ids = list(responses.keys())
                        print(f'\n继续升级设备: {self.device_ids}')
                        return True
                    elif choice == 'n':
                        print('\n取消升级')
                        return False
                    else:
                        print('无效输入，请输入 y 或 n')
                
            frame = self.receive_frame(100)  # 较短的超时时间用于轮询
            if frame is None:
                continue
                
            device_id = (frame.ID - CAN_OTA_RESP_ID) & 0x0F
            if device_id in self.device_ids and device_id not in responses:
                if frame.Data[2] == expected_resp:
                    responses[device_id] = True
                    print(f'设备 {device_id} 响应成功')
                else:
                    print(f'设备 {device_id} 响应错误: 0x{frame.Data[2]:02X}')
                    return False
                    
        return True
        
    def get_next_seq(self):
        """获取下一个序号"""
        seq = self.cmd_seq
        self.cmd_seq = (self.cmd_seq + 1) & 0xFF  # 0-255循环
        return seq
        
    def open_device(self):
        """打开CAN设备"""
        ret = self.canDLL.VCI_OpenDevice(self.device_type, self.device_ind, 0)
        if ret != STATUS_OK:
            print('打开设备失败')
            return False
        print('打开设备成功')
        return True
        
    def init_can(self):
        """初始化CAN通道"""
        # 波特率500Kbps
        # 使用Timing0和Timing1配置波特率
        # Timing0: 0x00 - 预分频为1
        # Timing1: 0x1C - 采样点位置87.5%
        vci_config = VCI_INIT_CONFIG(0x80000000, 0xFFFFFFFF, 0,
                                   0, 0x00, 0x14, 0)  # 波特率1000k
        ret = self.canDLL.VCI_InitCAN(self.device_type, self.device_ind, 
                                     self.can_ind, byref(vci_config))
        if ret != STATUS_OK:
            print('初始化CAN失败')
            return False
            
        ret = self.canDLL.VCI_StartCAN(self.device_type, self.device_ind, self.can_ind)
        if ret != STATUS_OK:
            print('启动CAN失败')
            return False
            
        print('初始化CAN成功')
        return True
        
    def send_frame(self, frame_id, data, len):
        """发送CAN帧"""
        send_frame = VCI_CAN_OBJ()
        send_frame.ID = frame_id
        send_frame.SendType = 1  # 正常发送
        send_frame.RemoteFlag = 0  # 数据帧
        send_frame.ExternFlag = 0  # 标准帧
        send_frame.DataLen = len
        
        # 打印调试信息
        # print(f"Sending CAN frame: ID=0x{frame_id:X}, Len={len}, Data=", end='')
        for i in range(len):
            send_frame.Data[i] = data[i]
            # print(f"{data[i]:02X} ", end='')
        # print()
            
        # 清空接收缓冲区
        self.canDLL.VCI_ClearBuffer(self.device_type, self.device_ind, self.can_ind)
        
        # 发送数据，最多重试3次
        for retry in range(3):
            ret = self.canDLL.VCI_Transmit(self.device_type, self.device_ind, 
                                          self.can_ind, byref(send_frame), 1)
            if ret == 1:
                # print(f"发送成功 (重试次数: {retry})")
                # time.sleep(0.01)  # 发送后等待10ms
                return True
            else:
                print(f"发送失败: ret={ret}, 重试中...")
                time.sleep(0.1)  # 失败后等待100ms再重试
        
        print("发送失败，已达到最大重试次数")
        return False
        
    def receive_frame(self, timeout_ms=1000):
        """接收CAN帧"""
        recv_frame = VCI_CAN_OBJ()
        start_time = time.time()
        
        while (time.time() - start_time) * 1000 < timeout_ms:
            ret = self.canDLL.VCI_Receive(self.device_type, self.device_ind,
                                         self.can_ind, byref(recv_frame), 1, 0)
            if ret > 0:
                # 打印接收到的数据
                print(f"Received CAN frame: ID=0x{recv_frame.ID:X}, Len={recv_frame.DataLen}, Data=", end='')
                for i in range(recv_frame.DataLen):
                    print(f"{recv_frame.Data[i]:02X} ", end='')
                print()
                return recv_frame
            # time.sleep(0.001)
            
        print("接收超时")
        return None
        
    def wait_response(self, expected_resp=CAN_OTA_RESP_OK, timeout_ms=5000):  # 增加超时时间
        """等待并验证响应"""
        frame = self.receive_frame(timeout_ms)
        if frame is None:
            print('响应超时')
            return False
            
        if frame.ID != CAN_OTA_RESP_ID:
            print(f'收到意外的帧ID: 0x{frame.ID:X}')
            return False
            
        status = frame.Data[2]  # 响应状态在第3个字节
        if status != expected_resp:
            print(f'响应错误: 0x{status:02X}')
            return False
            
        return True
        
    def send_start_cmd(self, file_size, crc32):
        """发送开始命令到所有设备"""
        print('发送开始命令到所有设备...')
        seq = self.get_next_seq()
        
        # 构造命令数据
        cmd_data = struct.pack('<BBHI', 
            CAN_OTA_CMD_START,  # cmd
            seq,                # seq
            0,                  # flags
            file_size          # param (文件大小)
        )
        
        # 发送到每个设备
        for device_id in self.device_ids:
            if not self.send_frame(self.get_cmd_id(device_id), cmd_data, 8):
                print(f'发送开始命令到设备 {device_id} 失败')
                return False
                
        # 等待所有设备响应
        return self.wait_all_responses(CAN_OTA_RESP_READY)
        
    def send_block_data(self, firmware_data, offset, block_size):
        """发送数据块"""
        retry_count = 0
        disconnected_devices = set()  # 记录断开的设备
        
        while retry_count < 3:  # 最多重试3次
            if retry_count > 0:
                print(f'\n正在进行第{retry_count}次重试...')
                
            # 发送数据块开始命令
            print('发送数据块开始命令...')
            packet_count = (block_size + 7) // 8  # 向上取整
            seq = self.get_next_seq()
            cmd_data = struct.pack('<BBHI',
                CAN_OTA_CMD_BLOCK_START,  # cmd
                seq,                      # seq
                0,                        # flags
                (packet_count << 16) | block_size  # param
            )
            
            # 发送到每个设备并等待响应
            responding_devices = set()  # 本次响应的设备
            for device_id in self.device_ids:
                if device_id in disconnected_devices:
                    continue  # 跳过已断开的设备
                    
                if not self.send_frame(self.get_cmd_id(device_id), cmd_data, 8):
                    print(f'发送数据块开始命令到设备 {device_id} 失败')
                    continue
                    
                # 等待响应
                frame = self.receive_frame(5000)
                if frame is None:
                    print(f'设备 {device_id} 响应超时')
                    continue
                    
                if frame.ID != self.get_resp_id(device_id):
                    print(f'收到意外的帧ID: 0x{frame.ID:X}')
                    continue
                    
                resp_seq = frame.Data[0] | (frame.Data[1] << 8)
                if resp_seq != seq:
                    print(f'响应序号不匹配: 期望={seq}, 收到={resp_seq}')
                    continue
                    
                if frame.Data[2] != CAN_OTA_RESP_OK:
                    print('数据块开始命令响应错误')
                    continue
                    
                responding_devices.add(device_id)
                
            # 检查是否有新的设备断开
            newly_disconnected = set(self.device_ids) - disconnected_devices - responding_devices
            if newly_disconnected:
                print(f'\n设备 {list(newly_disconnected)} 已断开连接')
                disconnected_devices.update(newly_disconnected)
                
                # 询问用户是否继续升级其他设备
                if responding_devices:
                    while True:
                        choice = input('\n是否继续升级在线的设备? (y/n): ').lower()
                        if choice == 'y':
                            print(f'\n继续升级设备: {list(responding_devices)}')
                            self.device_ids = list(responding_devices)  # 更新设备列表
                            break
                        elif choice == 'n':
                            print('\n用户取消升级')
                            return False
                        else:
                            print('无效输入，请输入 y 或 n')
                else:
                    print('\n没有设备响应,升级终止')
                    return False
            
            # 发送数据包
            sent_size = 0
            sent_packets = 0
            print('\n开始发送数据包:')
            
            # 分包发送
            while sent_size < block_size:
                # 准备数据包
                chunk_size = min(8, block_size - sent_size)
                chunk_data = firmware_data[offset + sent_size:offset + sent_size + chunk_size]
                
                # 发送数据内容到每个设备
                data_frame = bytearray(chunk_data)
                if len(data_frame) < 8:  # 如果数据不足8字节，填充0
                    data_frame.extend([0] * (8 - len(data_frame)))
                    
                # 发送到每个在线设备
                all_devices_ok = True
                for device_id in self.device_ids:
                    if not self.send_frame(self.get_data_id(device_id), data_frame, 8):
                        print(f'\n发送数据帧到设备 {device_id} 失败')
                        all_devices_ok = False
                        break
                        
                if not all_devices_ok:
                    break
                        
                sent_size += chunk_size
                sent_packets += 1
                
                # 打印进度
                if sent_packets % 16 == 0:  # 每16个包打印一次进度
                    print(f'\r传输进度: {sent_packets}/{packet_count}包 ({sent_size}/{block_size}字节)', end='\n')
                    
            if not all_devices_ok:
                retry_count += 1
                continue
                
            print('\n数据包发送完成')
            
            # 发送数据块结束命令
            print('发送数据块结束命令...')
            seq = self.get_next_seq()
            cmd_data = struct.pack('<BBHI',
                CAN_OTA_CMD_BLOCK_END,  # cmd
                seq,                    # seq
                0,                      # flags
                self.calculate_crc32(firmware_data[offset:offset + block_size])  # param (数据块CRC32)
            )
            
            # 发送到每个设备并等待响应
            responding_devices.clear()
            for device_id in self.device_ids:
                if not self.send_frame(self.get_cmd_id(device_id), cmd_data, 8):
                    print(f'发送数据块结束命令到设备 {device_id} 失败')
                    continue
                    
                # 等待响应
                frame = self.receive_frame(5000)
                if frame is None:
                    print(f'设备 {device_id} 响应超时')
                    continue
                    
                if frame.ID != self.get_resp_id(device_id):
                    print(f'收到意外的帧ID: 0x{frame.ID:X}')
                    continue
                    
                resp_seq = frame.Data[0] | (frame.Data[1] << 8)
                if resp_seq != seq:
                    print(f'响应序号不匹配: 期望={seq}, 收到={resp_seq}')
                    continue
                    
                status = frame.Data[2]
                if status == CAN_OTA_RESP_RETRY:
                    print(f'设备 {device_id} 请求重传')
                    continue
                elif status != CAN_OTA_RESP_OK:
                    print(f'设备 {device_id} 数据块结束命令响应错误: 0x{status:02X}')
                    continue
                    
                responding_devices.add(device_id)
                
            # 检查是否有新的设备断开
            newly_disconnected = set(self.device_ids) - disconnected_devices - responding_devices
            if newly_disconnected:
                print(f'\n设备 {list(newly_disconnected)} 已断开连接')
                disconnected_devices.update(newly_disconnected)
                
                # 询问用户是否继续升级其他设备
                if responding_devices:
                    while True:
                        choice = input('\n是否继续升级在线的设备? (y/n): ').lower()
                        if choice == 'y':
                            print(f'\n继续升级设备: {list(responding_devices)}')
                            self.device_ids = list(responding_devices)  # 更新设备列表
                            break
                        elif choice == 'n':
                            print('\n用户取消升级')
                            return False
                        else:
                            print('无效输入，请输入 y 或 n')
                else:
                    print('\n没有设备响应,升级终止')
                    return False
                    
            return True  # 数据块发送成功
            
        print('达到最大重试次数(3), 传输失败')
        return False  # 所有重试都失败
        
    def send_firmware_data(self, firmware_data):
        """发送固件数据"""
        total_size = len(firmware_data)
        sent_size = 0
        block_size = MAX_BLOCK_SIZE  # 2KB的数据块
        
        with tqdm(total=total_size, unit='B', unit_scale=True) as pbar:
            while sent_size < total_size:
                # 发送一个数据块
                current_block_size = min(block_size, total_size - sent_size)
                if not self.send_block_data(firmware_data, sent_size, current_block_size):
                    print('\n发送数据块失败')
                    return False
                    
                sent_size += current_block_size
                pbar.update(current_block_size)
                
        return True
        
    def send_end_cmd(self, crc32):
        """发送结束命令"""
        print('发送结束命令...')
        seq = self.get_next_seq()
        cmd_data = struct.pack('<BBHI',
            CAN_OTA_CMD_END, # cmd
            seq,             # seq
            0,              # flags
            crc32           # param (CRC32)
        )
        
        # 发送到每个设备
        for device_id in self.device_ids:
            if not self.send_frame(self.get_cmd_id(device_id), cmd_data, 8):
                print(f'发送结束命令到设备 {device_id} 失败')
                return False
                
            # 等待此设备的响应
            frame = self.receive_frame(5000)
            if frame is None:
                print(f'设备 {device_id} 响应超时')
                return False
                
            if frame.ID != self.get_resp_id(device_id):
                print(f'收到意外的帧ID: 0x{frame.ID:X}, 期望: 0x{self.get_resp_id(device_id):X}')
                return False
                
            if frame.Data[2] != CAN_OTA_RESP_VERIFY:
                print(f'设备 {device_id} 响应错误: 0x{frame.Data[2]:02X}')
                return False
                
            print(f'设备 {device_id} 固件校验成功')
            
        return True
        
    def calculate_crc32(self, data):
        """计算CRC32"""
        crc = 0xFFFFFFFF
        for b in data:
            crc = crc ^ b
            for _ in range(8):
                if crc & 1:
                    crc = (crc >> 1) ^ 0xEDB88320
                else:
                    crc = crc >> 1
        return ~crc & 0xFFFFFFFF
        
    def update_firmware(self, firmware_path, device_ids=None):
        """执行固件更新"""
        if device_ids:
            self.device_ids = device_ids
            
        # 记录所有设备的升级状态
        device_status = {device_id: {
            'status': 'pending',  # pending/success/failed/disconnected
            'stage': 'init',      # init/boot/transfer/verify
            'error': None         # 错误信息
        } for device_id in self.device_ids}
            
        try:
            print(f'准备更新设备: {self.device_ids}')
            
            # 读取固件文件
            with open(firmware_path, 'rb') as f:
                firmware_data = f.read()
                
            print(f'固件大小: {len(firmware_data)} 字节')
            
            # 计算CRC32
            crc32 = self.calculate_crc32(firmware_data)
            print(f'固件CRC32: 0x{crc32:08X}')
            
            # 打开设备
            if not self.open_device():
                for device_id in device_status:
                    device_status[device_id].update({'status': 'failed', 'error': '打开CAN设备失败'})
                return False
                
            # 初始化CAN
            if not self.init_can():
                for device_id in device_status:
                    device_status[device_id].update({'status': 'failed', 'error': '初始化CAN失败'})
                return False
                
            # 向指定设备发送开始命令,让设备进入boot模式
            print('\n向指定设备发送开始命令,使设备进入boot模式...')
            cmd_data = struct.pack('<BBHI',
                CAN_OTA_CMD_START,  # cmd
                self.get_next_seq(),  # seq
                0,                  # flags
                len(firmware_data)  # param (文件大小)
            )
            
            # 分别向每个指定的设备发送命令
            for device_id in self.device_ids:
                print(f'发送开始命令到设备 {device_id}...')
                if not self.send_frame(self.get_cmd_id(device_id), cmd_data, 8):
                    device_status[device_id].update({
                        'status': 'failed',
                        'error': '发送开始命令失败'
                    })
                    continue
                
            # 等待2秒,让设备重启进入boot模式
            print('等待2秒,让设备重启进入boot模式...')
            time.sleep(2)
            
            # 再次向指定设备发送开始命令,检测在线设备
            print('\n再次发送开始命令,检测在线设备...')
            cmd_data = struct.pack('<BBHI',
                CAN_OTA_CMD_START,  # cmd
                self.get_next_seq(),  # seq
                0,                  # flags
                len(firmware_data)  # param (文件大小)
            )
            
            # 分别向每个指定的设备发送命令
            for device_id in self.device_ids:
                print(f'发送开始命令到设备 {device_id}...')
                if not self.send_frame(self.get_cmd_id(device_id), cmd_data, 8):
                    device_status[device_id].update({
                        'status': 'failed',
                        'error': '发送开始命令失败'
                    })
                    continue
                
            # 收集5秒内的所有响应
            print('等待设备响应(5秒)...')
            online_devices = set()
            start_time = time.time()
            
            while (time.time() - start_time) < 5:
                frame = self.receive_frame(100)  # 使用较短的超时时间来轮询
                if frame is not None:
                    # 检查是否是响应帧(0x500-0x50F)
                    if (frame.ID & 0xFF0) == CAN_OTA_RESP_ID:
                        device_id = frame.ID & 0x0F
                        if device_id in self.device_ids and device_id not in online_devices:
                            if frame.Data[2] == CAN_OTA_RESP_READY:  # 检查是否是READY响应
                                online_devices.add(device_id)
                                device_status[device_id].update({
                                    'status': 'pending',
                                    'stage': 'boot',
                                })
                                print(f'设备 {device_id} 在线且已进入boot模式')
            
            # 更新离线设备状态
            for device_id in set(self.device_ids) - online_devices:
                device_status[device_id].update({
                    'status': 'failed',
                    'stage': 'boot',
                    'error': '设备未响应或未进入boot模式'
                })
            
            # 更新设备列表为在线设备
            self.device_ids = sorted(list(online_devices))
            
            if not self.device_ids:
                print('\n没有设备响应,升级终止')
                return False
                
            print(f'\n检测到在线设备: {self.device_ids}')
            choice = input('是否继续升级? (y/n): ').lower()
            if choice != 'y':
                print('\n用户取消升级')
                return False
                
            # 发送固件数据
            print('\n开始传输固件数据...')
            for device_id in self.device_ids:
                device_status[device_id].update({'stage': 'transfer'})
                
            if not self.send_firmware_data(firmware_data):
                print('\n发送固件数据失败,升级终止')
                # 更新传输失败的设备状态
                for device_id in self.device_ids:
                    if device_status[device_id]['status'] == 'pending':
                        device_status[device_id].update({
                            'status': 'failed',
                            'error': '固件传输失败'
                        })
                return False
                
            # 发送结束命令
            print('\n发送结束命令...')
            for device_id in self.device_ids:
                device_status[device_id].update({'stage': 'verify'})
                
            if not self.send_end_cmd(crc32):
                print('\n结束命令失败,升级终止')
                # 更新校验失败的设备状态
                for device_id in self.device_ids:
                    if device_status[device_id]['status'] == 'pending':
                        device_status[device_id].update({
                            'status': 'failed',
                            'error': '固件校验失败'
                        })
                return False
                
            # 更新成功完成升级的设备状态
            for device_id in self.device_ids:
                if device_status[device_id]['status'] == 'pending':
                    device_status[device_id].update({
                        'status': 'success',
                        'error': None
                    })
            
            # 打印最终升级状态报告
            print('\n升级完成状态报告:')
            print('----------------------------------------')
            for device_id, status in device_status.items():
                status_str = {
                    'success': '成功',
                    'failed': '失败',
                    'disconnected': '断开',
                    'pending': '未完成'
                }.get(status['status'], '未知')
                
                stage_str = {
                    'init': '初始化',
                    'boot': '进入Boot',
                    'transfer': '数据传输',
                    'verify': '校验'
                }.get(status['stage'], '未知')
                
                print(f'设备 {device_id}:')
                print(f'  状态: {status_str}')
                print(f'  阶段: {stage_str}')
                if status['error']:
                    print(f'  错误: {status["error"]}')
                print('----------------------------------------')
            
            # 统计成功和失败数量
            success_count = sum(1 for s in device_status.values() if s['status'] == 'success')
            failed_count = len(device_status) - success_count
            
            print(f'\n升级统计:')
            print(f'总设备数: {len(device_status)}')
            print(f'成功数量: {success_count}')
            print(f'失败数量: {failed_count}')
            
            return success_count > 0
            
        except Exception as e:
            print(f'\n更新失败: {str(e)}')
            # 更新所有未完成设备的状态
            for device_id in device_status:
                if device_status[device_id]['status'] == 'pending':
                    device_status[device_id].update({
                        'status': 'failed',
                        'error': f'系统异常: {str(e)}'
                    })
            return False
            
        finally:
            # 关闭设备
            self.canDLL.VCI_CloseDevice(self.device_type, self.device_ind)

def main():
    # 创建命令行参数解析器
    parser = argparse.ArgumentParser(description='CAN OTA固件更新工具')
    parser.add_argument('--firmware', '-f', type=str, default="build/application.bin",
                       help='固件文件路径 (默认: build/application.bin)')
    group = parser.add_mutually_exclusive_group(required=True)
    group.add_argument('--devices', '-d', type=int, nargs='+',
                       help='要升级的设备ID列表，例如: -d 1 2 3')
    group.add_argument('--all', '-a', action='store_true',
                       help='更新所有设备 (ID 0-15)')
    
    # 解析命令行参数
    args = parser.parse_args()
    
    # 检查固件文件是否存在
    if not os.path.exists(args.firmware):
        print(f'错误: 固件文件不存在: {args.firmware}')
        return
        
    # 确定要升级的设备列表
    if args.all:
        device_ids = list(range(16))  # 0-15的所有设备ID
        print('已选择更新所有设备 (ID 0-15)')
    else:
        device_ids = args.devices
        # 检查设备ID范围
        invalid_ids = [id for id in device_ids if id < 0 or id > 15]
        if invalid_ids:
            print(f'错误: 无效的设备ID {invalid_ids}，设备ID必须在0-15范围内')
            return
    
    print(f'准备更新以下设备: {device_ids}')
    print(f'使用固件文件: {args.firmware}')
    
    # 确认是否继续
    choice = input('是否继续? (y/n): ').lower()
    if choice != 'y':
        print('用户取消操作')
        return
        
    # 创建更新器实例并执行更新
    updater = CANOTAUpdater(device_ids)
    updater.update_firmware(args.firmware)

if __name__ == '__main__':
    main()
 
